
/*
 uart, channel: 
 消息发送之前使用 hdlc 编码, 这样在字符流中能够定义边界和长度.
 编码之前的最大的消息长度是 64 个字节.
 编码之后 应该不会超过 128 个字节.
*/


#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>


#include "rp2040.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"

#include "umsg.h"
#include "utchan.h"

/*
0x7E : 标识 frame 的开始和结束.

需要转义的序列如下:
0x7E : 0x7D 0x5E
0x7D : 0x7D 0x5D

return : 编码之后的字节长度.
*/
static int hdlc_encode( umsg_t * pmsg, uint8_t * pout )
{
    int i;
    int ofs;
    uint8_t temp;

    /* frame begin. */
    ofs = 0;
    pout[ofs] = 0x7E;
    ofs += 1;

    /**/
    for ( i=0; i<pmsg->mlen; i++ ) {

        temp = pmsg->data[i];

        if ( temp == 0x7E ) {
            
            pout[ofs] = 0x7D;
            ofs += 1;

            pout[ofs] = 0x5E;
            ofs += 1;

        } else if ( temp == 0x7D ) {

            pout[ofs] = 0x7D;
            ofs += 1;

            pout[ofs] = 0x5D;
            ofs += 1;

        } else {

            pout[ofs] = temp;
            ofs += 1;
        }

    }

    /* frame end.*/
    pout[ofs] = 0x7E;
    ofs += 1;
    return ofs;
}


/*
  在接收新的消息之前, 重置状态.
*/
static int hdlc_reset( umsg_t * pmsg )
{
    pmsg->type = 0;
    pmsg->mlen = 0;
    return 0;
}



/*
  type = 0 : 没有找到开始的 0x7E..
  type = 1 : 已经找到开始 0x7E..
  type = 0x7D : 开始之后, 刚刚吃进一个 escape 0x7D 字符.

  return : 0, 解码成功, 生成一个完整消息.
*/
static int hdlc_decode( uint8_t ich, umsg_t * pmsg )
{
    if ( ich == 0x7E ) {

        if ( pmsg->type == 0 ) {
            
            // frame begin...
            pmsg->type = 1;
            return 2;

        } else if ( pmsg->type == 1 ) {
            
            if ( pmsg->mlen == 0 ) {
                // 某种错误恢复.
                return 1;
            } else {

                // frame end..
                return 0;   
            }

        } else {

            // error..
            return -1;
        }
        
    }

    if ( pmsg->mlen >= 64 ) {
        // too long..
        return -2;
    }


    if ( pmsg->type == 0 ) {

        // 没有找到开始 0x7E, 仅仅丢弃这个输入.
        return 1;

    } else if ( pmsg->type == 1 ) {

        if ( ich == 0x7D ) {
            
            pmsg->type = 0x7D;

        } else {

            pmsg->data[pmsg->mlen] = ich;
            pmsg->mlen += 1;
        
        }

        return 3;

    } else if ( pmsg->type == 0x7D ) {

        if ( ich == 0x5E ) {

            pmsg->type = 1;
            pmsg->data[pmsg->mlen] = 0x7E;
            pmsg->mlen += 1;
            return 4;

        } else if ( ich == 0x5D ) {
            
            pmsg->type = 1;
            pmsg->data[pmsg->mlen] = 0x7D;
            pmsg->mlen += 1;
            return 5;

        } else {
            return -3;
        }

    } else {

        // error
        return -4;

    }

}


extern void umsg_send_to_host( void * pifc, int idx, umsg_t * pmsg );

static umsg_t * rxmsg = NULL;
static uint32_t rxcnt = 0;

/* send */
static uqueue_t me2fpga;
static uint32_t txcnt = 0;
static uint8_t  txpad[128];
static int txsiz = 0;
static int txofs = 0;


static inline void utchan_intr_recv( uart_hw_t * puart )
{
    int iret;
    uint32_t temp;

    while ( (puart->fr & UART_UARTFR_RXFE_BITS) == 0 ) {
            
        rxcnt += 1;
        temp = puart->dr;

        /* todo : error flags */


        /**/
        iret = hdlc_decode( (uint8_t)(temp & 0xFF), rxmsg );
        
        if ( iret < 0 ) {
            hdlc_reset(rxmsg);
            continue;
        }

        if ( iret == 0 ) {

            umsg_send_to_host( NULL, 0, rxmsg );

            /* 准备接收下一个消息. */
            rxmsg = umsg_alloc();
            hdlc_reset(rxmsg);
        }

    }

    return;
}


static inline void utchan_intr_send( uart_hw_t * puart )
{
    umsg_t * pmsg;

next_msg:

    if ( txsiz == 0 ) {
        
        /* get next msg */
        pmsg = umsg_q_deque( &me2fpga );
        if ( pmsg == NULL ) {
            uart_set_irq_enables( uart0, true, false );
            return;
        }

        /**/
        txsiz = hdlc_encode( pmsg, txpad );
        txofs = 0;
        umsg_free( pmsg );
    }

    /* trans fifo full == 0 */
    while ( ( puart->fr & UART_UARTFR_RXFF_BITS) == 0) {
            
        /**/
        if ( txofs >= txsiz ) {
            txsiz = 0;
            goto next_msg;
        }

        puart->dr = txpad[txofs];
        txofs += 1;
    }

    return;
}


void  Uart_0_Handler( void )
{
    uart_hw_t * puart;
    uint32_t msts;

    /**/
    puart = uart0_hw;
    msts = puart->mis;
    txcnt += 1;

    /* recv char */
    if ( (msts & (UART_UARTMIS_RTMIS_BITS | UART_UARTMIS_RXMIS_BITS)) != 0 ) {
        utchan_intr_recv( puart );
    }

    /* send char */
    if ( (msts & UART_UARTMIS_TXMIS_BITS) != 0 ) {
        utchan_intr_send( puart );
    }
    
    return;
}


int utchan_svc_send( umsg_t * pmsg )
{
    pmsg->data[pmsg->mlen] = pmsg->type & 0x3F;
    pmsg->mlen += 1;

    /* todo : interrupt mask for trans */
    umsg_q_enque( &me2fpga, pmsg );
    utchan_intr_send( uart0_hw );
    return 0;
}



int utchan_init( void )
{
    rxmsg = umsg_alloc();
    hdlc_reset(rxmsg);

    /**/
    umsg_q_init( &me2fpga );
    txsiz = 0;
    txofs = 0;

    /* clock */
    hw_set_bits( &clocks_hw->wake_en1, CLOCKS_WAKE_EN1_CLK_SYS_UART0_BITS | CLOCKS_WAKE_EN1_CLK_PERI_UART0_BITS );

    /* gpio-0:uart0-tx,  gpio-1:uart0-rx */
    gpio_pull_up( 12 );
    gpio_pull_up( 13 );
    gpio_set_function( 12, GPIO_FUNC_UART );
    gpio_set_function( 13, GPIO_FUNC_UART );

    /**/
    uart_init( uart0, 115200 );

    /* interrupt enable */
    uart_set_irq_enables( uart0, true, false );
    NVIC_EnableIRQ( UART0_IRQ_IRQn );
    return 0;
}

